package article11.equals.hashcode;

import java.util.Objects;

/**
 * Class with a typical equals method
 *
 * @Author: cctv
 * @Date: 2019/4/3 14:16
 */
public class PhoneNumber {

    private final short areaCode, prefix, lineNum;
    private int hashCode;

    public PhoneNumber(int areaCode, int prefix, int lineNum) {
        this.areaCode = rangeCheck(areaCode, 9999, "area code");
        this.prefix = rangeCheck(prefix, 9999, "prefix");
        this.lineNum = rangeCheck(lineNum, 9999, "lin num");
    }

    private static short rangeCheck(int val, int max, String arg) {
        if (val < 0 || val > max) {
            throw new IllegalArgumentException(arg + ":" + val);
        }
        return (short) val;
    }

    @Override
    public boolean equals(Object o) {
        //使用==操作符检查“参数是否为这个对象的引用”
        if (o == this) {
            return true;
        }
        //使用instanceof操作符检查“参数是否为正确的类型”
        if (!(o instanceof PhoneNumber)) {
            return false;
        }
        //把参数转换成正确的类型
        PhoneNumber pn = (PhoneNumber) o;
        //对于该类中的每一个“关键”域，检查参数中的域是否与该对象中对应的域相匹配
        return pn.lineNum == lineNum && pn.prefix == prefix && pn.areaCode == areaCode;
    }

    /**
     * 之所以选择 31 ，是因为它是一个奇素数。如果乘数是偶数，并且乘法、溢出的话，信息就会丢失，因为与2相乘等价
     * 于移位运算，使用素数的好处并不很明显，但是习惯上都使用素数来计算散列结果，有个很好的特性，
     * 即用移位和减法来代替乘法，可以得到更好的性能 31*i==(i<<5)-i。 现代的虚拟机可以自动完成这种优化
     * <p>
     * 如果类是不可变的，并且计算hashcode开销也比较大，就应该考虑吧hashcode缓存在对象内部
     *
     * @return
     */
    @Override
    public int hashCode() {
//        最糟糕的合法哈希代码实现
//        return 42;

        //一行代码得到hashCode,不过运行速度要更慢些因为它们会引发数组的创建，
        //以便传入数目可变的参数， 如果参数中有基本类型，还需要装箱和拆箱
//        return Objects.hash(lineNum,prefix,areaCode);

        int result = hashCode;
        if (result == 0) {
            result = Short.hashCode(areaCode);
            result = 31 * result + Short.hashCode(prefix);
            result = 31 * result + Short.hashCode(lineNum);
        }
        return result;
    }


}
